package com.rd.sys.service.analysis.statistic.impl;

import java.io.*;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import com.rd.sys.dto.client.store.EventModel;
import com.rd.sys.service.analysis.event.EventService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import baseproj.common.exception.BaseServiceException;
import baseproj.common.mybatis.page.PageParameter;

import com.rd.sys.common.base.CommonConstants;
import com.rd.sys.common.utils.JacksonMapper;
import com.rd.sys.common.utils.cache.AnalysisCache;
import com.rd.sys.common.utils.client.AnalysisPathUtils;
import com.rd.sys.dto.client.data.GazeData;
import com.rd.sys.dto.client.data.Point2D;
import com.rd.sys.dto.client.store.Material;
import com.rd.sys.dto.client.store.Result;
import com.rd.sys.dto.client.store.Scheme;
import com.rd.sys.dto.client.store.statistic.StatisticFixationData;
import com.rd.sys.dto.client.store.statistic.StatisticSaccadeData;
import com.rd.sys.dto.client.store.statistic.TempIndex;
import com.rd.sys.service.analysis.result.ResultService;
import com.rd.sys.service.analysis.scheme.SchemeService;
import com.rd.sys.service.analysis.statistic.StatisticService;
import org.springframework.util.CollectionUtils;

@Service
@Transactional(rollbackFor = {Exception.class})
public class StatisticServiceImpl implements StatisticService {

    protected static final Logger logger = LoggerFactory.getLogger(StatisticServiceImpl.class);

    /**
     * 统计数据精度格式化
     */
    private static DecimalFormat decimalFormat = new DecimalFormat(
            CommonConstants.AnalysisConstats.STATISTIC_DECIMAL_PRECISION);

    @Autowired
    private ResultService resultService;

    @Autowired
    private SchemeService schemeService;

    @Autowired
    private EventService eventService;

    /**
     * 查询注视点统计数据
     *
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param resultCode
     * @param materialCode
     * @param page
     * @return
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<StatisticFixationData> queryFixationDataList(String userCode, String projectCode, String schemeCode,
                                                             String resultCode, String materialCode, PageParameter page) throws Exception {
        List<StatisticFixationData> fixDataList = new LinkedList<StatisticFixationData>();
        List<StatisticFixationData> fixTempDataList = new LinkedList<StatisticFixationData>();
        try {
            String key_prifix = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + schemeCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT; // 缓存关键字前缀

            /** 当选择了结果时,直接加载该结果的数据 */
            if (StringUtils.isNotBlank(resultCode)) {
                fixTempDataList.addAll(AnalysisCache.DataStatisticFixationCache.get(key_prifix + resultCode));
            } else {
                /** 否则则遍历加载该所有结果的数据 */
                List<Result> resultList = resultService.queryResultList(userCode, projectCode, schemeCode);
                for (int i = 0; i < resultList.size(); i++) {
                    fixTempDataList.addAll(AnalysisCache.DataStatisticFixationCache.get(key_prifix
                            + resultList.get(i).getResultCode()));
                }
            }

            /** 当选择了素材时,直接遍历过滤,只显示该素材的数据 */
            if (StringUtils.isNotBlank(materialCode)) {
                Iterator<StatisticFixationData> iterator = fixTempDataList.iterator();
                StatisticFixationData fixData = null;
                while (iterator.hasNext()) {
                    fixData = iterator.next();
                    if (!fixData.getMaterialCode().equals(materialCode)) { // 不等于则过滤掉
                        iterator.remove();
                    }
                }
            }

            /** 按分页条件过滤 */
            calcDataList(fixTempDataList, fixDataList, page);

        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception e) {
            logger.error("query statistic of fixation error.", e);
            throw new RuntimeException(e);
        }
        return fixDataList;
    }

    /**
     * 查询扫视点统计数据
     *
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param resultCode
     * @param materialCode
     * @param page
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<StatisticSaccadeData> querySaccadeDataList(String userCode, String projectCode, String schemeCode,
                                                           String resultCode, String materialCode, PageParameter page) throws Exception {
        List<StatisticSaccadeData> sacDataList = new LinkedList<StatisticSaccadeData>();
        List<StatisticSaccadeData> sacTempDataList = new LinkedList<StatisticSaccadeData>();
        try {
            String key_prifix = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + schemeCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT; // 缓存关键字前缀

            /** 当选择了结果时,直接加载该结果的数据 */
            if (StringUtils.isNotBlank(resultCode)) {
                sacTempDataList.addAll(AnalysisCache.DataStatisticSaccadeCache.get(key_prifix + resultCode));
            } else {
                /** 否则则遍历加载所有结果的数据 */
                List<Result> resultList = resultService.queryResultList(userCode, projectCode, schemeCode);
                for (int i = 0; i < resultList.size(); i++) {
                    sacTempDataList.addAll(AnalysisCache.DataStatisticSaccadeCache.get(key_prifix
                            + resultList.get(i).getResultCode()));
                }
            }

            /** 当选择了素材时,直接遍历过滤,只显示该素材的数据 */
            if (StringUtils.isNotBlank(materialCode)) {
                Iterator<StatisticSaccadeData> iterator = sacTempDataList.iterator();
                StatisticSaccadeData sacData = null;
                while (iterator.hasNext()) {
                    sacData = iterator.next();
                    if (!sacData.getMaterialCode().equals(materialCode)) { // 不等于则过滤掉
                        iterator.remove();
                    }
                }
            }

            /** 按分页条件过滤 */
            calcDataList(sacTempDataList, sacDataList, page);

        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception e) {
            logger.error("query statistic of saccade error.", e);
            throw new RuntimeException(e);
        }
        return sacDataList;
    }

    /**
     * 查询素材统计数据
     *
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param resultCode
     * @param materialCode
     * @param page
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<Material> queryMaterialDataList(String userCode, String projectCode, String schemeCode,
                                                String resultCode, String materialCode, PageParameter page) throws Exception {
        List<Material> materialDataList = new LinkedList<Material>();
        List<Material> tempDataList = new LinkedList<Material>();
        try {
            tempDataList.addAll(schemeService.queryMaterialList(userCode, projectCode, schemeCode));

            /** 当选择了素材时,直接遍历过滤,只显示该素材 */
            if (StringUtils.isNotBlank(materialCode)) {
                Iterator<Material> iterator = tempDataList.iterator();
                Material material = null;
                while (iterator.hasNext()) {
                    material = iterator.next();
                    if (!material.getCode().equals(materialCode)) {
                        iterator.remove();
                    }
                }
            }

            /** 按分页条件过滤 */
            calcDataList(tempDataList, materialDataList, page);

        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception e) {
            logger.error("query statistic of material error.", e);
            throw new RuntimeException(e);
        }
        return materialDataList;
    }

    /**
     * 查询结果统计数据
     *
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param resultCode
     * @param page
     * @return
     * @throws Exception
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<Result> queryResultDataList(String userCode, String projectCode, String schemeCode, String resultCode,
                                            PageParameter page) throws Exception {
        List<Result> resultDataList = new LinkedList<Result>();
        List<Result> tempDataList = new LinkedList<Result>();
        try {
            if (StringUtils.isNotBlank(resultCode)) { // 当选择了结果时,直接遍历过滤,只显示该结果
                tempDataList.add(resultService.queryResult(userCode, projectCode, schemeCode, resultCode));
            } else {
                tempDataList.addAll(resultService.queryResultList(userCode, projectCode, schemeCode));
            }

            /** 计算属性详情 */
            resultService.calcResultAttribute(userCode, projectCode, schemeCode, tempDataList);

            /** 按分页条件过滤 */
            calcDataList(tempDataList, resultDataList, page);

        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception e) {
            logger.error("query statistic of result error.", e);
            throw new RuntimeException(e);
        }
        return resultDataList;
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<EventModel> queryEventDataList(String userCode, String projectCode, String schemeCode, String resultCode, PageParameter page) throws Exception {
        List<EventModel> resultDataList = new LinkedList<EventModel>();
        List<EventModel> tempDataList = new ArrayList<EventModel>();

        if (StringUtils.isNotEmpty(resultCode)) {
            List<EventModel> emList = this.eventService.queryEventList(userCode, projectCode, schemeCode, resultCode);
            tempDataList.addAll(emList);
        } else {
            List<EventModel> emList = this.eventService.queryEventList(userCode, projectCode, schemeCode);
            tempDataList.addAll(emList);
        }
        /** 按分页条件过滤 */
        calcDataList(tempDataList, resultDataList, page);
        return resultDataList;
    }

    /**
     * 从注视点数据文件中加载注视点统计信息
     *
     * @param key
     * @return
     * @throws Exception
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<StatisticFixationData> readFixationDataList(String key) throws Exception {
        List<StatisticFixationData> statisticFixationDataList = new LinkedList<StatisticFixationData>(); // 注视点统计数据集
        List<GazeData> alreadyFixStatList = new LinkedList<GazeData>();
        StatisticFixationData statisticFixationData = null;
        GazeData gaze = null;

        /** key由usercode、projectcode、schmecode、resultcode组成 */
        String[] keys = key.split(CommonConstants.AnalysisConstats.CACHE_SPLIT);
        String userCode = keys[0];
        String projectCode = keys[1];
        String schemeCode = keys[2];
        String resultCode = keys[3];

        // 获取点击事件
        List<EventModel> eventModels = queryEvent(userCode,projectCode,schemeCode,resultCode);

        /** 从注视点统计数据文件中读取出来 */
        BufferedReader rawReader = null;
        try {
            TempIndex materialIndex = new TempIndex(); // 记录素材索引号,索引会在计算过程中改变,故需使用TempIndex对象封装
            Long baseTime = null;
            Scheme currentScheme = schemeService.queryScheme(userCode, projectCode, schemeCode);
            Result currentResult = resultService.queryResult(userCode, projectCode, schemeCode, resultCode);
            long offset = 0;
            for (Material material : currentScheme.getMaterials()){
                for (EventModel model : eventModels){
                    if (material.getCode().equalsIgnoreCase(model.getMaterialCode())){
                        material.setDuration(Long.valueOf(model.getIntervalTime()));
                        material.setOffset(offset);
                        break;
                    }
                }
                offset += material.getDuration();
            }
            File rawFile = new File(AnalysisPathUtils.findResultRawDataFileName(userCode, projectCode, schemeCode,
                    resultCode));

            rawReader = new BufferedReader(new InputStreamReader(new FileInputStream(rawFile),
                    CommonConstants.SysParamConstants.SYS_CHARSET));
            String line = null;
            while ((line = rawReader.readLine()) != null) {
                gaze = (GazeData) JacksonMapper.json2Bean(line, GazeData.class);

                if (baseTime == null) {
                    baseTime = gaze.timeStamp; // 基础时间点就是第一个gazedata的上报时间
                }

                /** 生成注视点统计数据 */
                statisticFixationData = this.judgeStatisticFixationData(alreadyFixStatList, gaze, currentScheme,
                        materialIndex, baseTime, currentResult, eventModels);
                if (statisticFixationData != null) {
                    statisticFixationData.setNumber(statisticFixationDataList.size() + 1); // 设置序号
                    statisticFixationDataList.add(statisticFixationData);
                }
                statisticFixationData = null;

                gaze = null;
            }

            if (alreadyFixStatList.size() > 0) { // 当最后一条原始数据读取完后,若待处理的注视点数据集中还有数据,则生成一个新的注视点信息
                statisticFixationData = this.calcStatisticFixationData(alreadyFixStatList, currentScheme, currentScheme
                        .getMaterials().get(currentScheme.getMaterials().size() - 1), currentResult, baseTime);
                if (statisticFixationData != null) {
                    statisticFixationData.setNumber(statisticFixationDataList.size() + 1);
                    statisticFixationDataList.add(statisticFixationData);
                }
            }

        } catch (Exception ex) {
            logger.error("", ex);
        } finally {
            if (rawReader != null) {
                try {
                    rawReader.close();
                    rawReader = null;
                } catch (Exception ex) {
                    logger.error("close rawdata file reader error ", ex);
                }
            }
        }
        return statisticFixationDataList;
    }

    /**
     * 从扫视点数据文件中加载扫视点统计信息
     *
     * @param key
     * @return
     * @throws Exception
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<StatisticSaccadeData> readSaccadeDataList(String key) throws Exception {
        List<StatisticSaccadeData> statisticSaccadeDataList = new LinkedList<StatisticSaccadeData>(); // 扫视点统计数据集
        List<GazeData> alreadySacStatList = new LinkedList<GazeData>();
        StatisticSaccadeData statisticSaccadeData = null;
        GazeData gaze = null;

        /** key由usercode、projectcode、schmecode、resultcode组成 */
        String[] keys = key.split(CommonConstants.AnalysisConstats.CACHE_SPLIT);
        String userCode = keys[0];
        String projectCode = keys[1];
        String schemeCode = keys[2];
        String resultCode = keys[3];

        /** 从扫视点统计数据文件中读取出来 */
        BufferedReader rawReader = null;
        try {
            TempIndex materialIndex = new TempIndex(); // 记录素材索引号,索引会在计算过程中改变,故需使用TempIndex对象封装
            Long baseTime = null;
            Scheme currentScheme = schemeService.queryScheme(userCode, projectCode, schemeCode);
            Result currentResult = resultService.queryResult(userCode, projectCode, schemeCode, resultCode);
            // 获取点击事件
            List<EventModel> eventModels = queryEvent(userCode,projectCode,schemeCode,resultCode);
            long offset = 0;
            for (Material material : currentScheme.getMaterials()){
                for (EventModel model : eventModels){
                    if (material.getCode().equalsIgnoreCase(model.getMaterialCode())){
                        material.setDuration(Long.valueOf(model.getIntervalTime()));
                        material.setOffset(offset);
                    }
                    offset += material.getDuration();
                }
            }
            File rawFile = new File(AnalysisPathUtils.findResultRawDataFileName(userCode, projectCode, schemeCode,
                    resultCode));

            rawReader = new BufferedReader(new InputStreamReader(new FileInputStream(rawFile),
                    CommonConstants.SysParamConstants.SYS_CHARSET));
            String line = null;
            while ((line = rawReader.readLine()) != null) {
                gaze = (GazeData) JacksonMapper.json2Bean(line, GazeData.class);

                if (baseTime == null) {
                    baseTime = gaze.timeStamp; // 基础时间点就是第一个gazedata的上报时间
                }

                /** 生成扫视点统计数据 */
                statisticSaccadeData = this.judgeStatisticSaccadeData(alreadySacStatList, gaze, currentScheme,
                        materialIndex, baseTime, currentResult, eventModels);
                if (statisticSaccadeData != null) {
                    statisticSaccadeData.setNumber(statisticSaccadeDataList.size() + 1);
                    statisticSaccadeDataList.add(statisticSaccadeData);
                }
                statisticSaccadeData = null;

                gaze = null;
            }

            if (alreadySacStatList.size() > 0) { // 当最后一条原始数据读取完后,若待处理的扫视点数据集中还有数据,则生成一个新的扫视点信息
                statisticSaccadeData = this.calcStatisticSaccadeData(alreadySacStatList, currentScheme, currentScheme
                        .getMaterials().get(currentScheme.getMaterials().size() - 1), currentResult, baseTime);
                if (statisticSaccadeData != null) {
                    statisticSaccadeData.setNumber(statisticSaccadeDataList.size() + 1);
                    statisticSaccadeDataList.add(statisticSaccadeData);
                }
            }

        } catch (Exception ex) {
            logger.error("", ex);
        } finally {
            if (rawReader != null) {
                try {
                    rawReader.close();
                    rawReader = null;
                } catch (Exception ex) {
                    logger.error("close rawdata file reader error ", ex);
                }
            }
        }

        return statisticSaccadeDataList;
    }

    private List<EventModel> queryEvent(String userCode, String projectCode, String schemeCode, String resultCode) {
        List<EventModel> list = new ArrayList<EventModel>();
        String key = AnalysisPathUtils.findResultDataPath(userCode,projectCode,schemeCode,resultCode)+CommonConstants.SysParamConstants.FILE_SEPARATOR+"EventInfo.json";
        try {
            InputStreamReader fileReader = new InputStreamReader(new FileInputStream(key),"utf-8");
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String str = null;
            while ((str = bufferedReader.readLine()) != null){
                EventModel eventModel = (EventModel) JacksonMapper.json2Bean(str,EventModel.class);
                list.add(eventModel);
            }
        } catch (IOException e) {
//            e.printStackTrace();
        }
        if (list.isEmpty()){
            logger.error("没有加载到EventInfo.json或者文件为空");
        }
        return list;
    }

    /**
     * 对下一个GazeData进行判断,看是否生成注视点
     *
     * @param alreadyFixStatList
     * @param waitGazeData
     * @param currentScheme
     * @param materialIndex
     * @param baseTime
     * @param currentResult
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public StatisticFixationData judgeStatisticFixationData(List<GazeData> alreadyFixStatList, GazeData waitGazeData,
                                                            Scheme currentScheme, TempIndex materialIndex, Long baseTime,
                                                            Result currentResult,List<EventModel> eventModels) throws Exception {
        /** 1. 数据准备 */
        Material currentMaterial = currentScheme.getMaterials().get(materialIndex.index);
        long startTime = baseTime + currentMaterial.getOffset(); // 当前素材开始时间
        long endTime = startTime + currentMaterial.getDuration(); // 当前素材结束时间
        // 获取点击事件的开始结束事件，如果点击事件没有则使用方案的开始结束事件
//        for (EventModel model : eventModels){
//            if (currentMaterial.getName().equalsIgnoreCase(model.getMaterialName())){
//                endTime = Long.parseLong(model.getEventTime());
//                startTime = endTime - Long.parseLong(model.getIntervalTime());
//            }
//        }


        boolean changeMaterial = true; // 是否切换了素材
        if (waitGazeData.timeStamp >= startTime && waitGazeData.timeStamp < endTime) {
            changeMaterial = false;
        }

        /** 2.若待处理数据集为空,则不生成统计数据 */
        if (alreadyFixStatList.isEmpty()) {
            if (waitGazeData.isFixated) { //
                alreadyFixStatList.add(waitGazeData);
            }
            return null;
        }

        StatisticFixationData statisticData = null;
        /** 3.若待处理数据isFixated标识为false,则直接计算已有的数据集,生成新的统计注视点 */
        if (!waitGazeData.isFixated) {
            statisticData = calcStatisticFixationData(alreadyFixStatList, currentScheme, currentMaterial,
                    currentResult, baseTime);
        } else {
            /** 4.若待处理数据isFixated标识为true,还需进一步分析包含关系 */
            if (!contains(alreadyFixStatList.get(0).smoothedCoordinates, waitGazeData.smoothedCoordinates)) {
                // 若未在之前的注视点范围内,则生成新的统计注视点
                statisticData = calcStatisticFixationData(alreadyFixStatList, currentScheme, currentMaterial,
                        currentResult, baseTime);
            } else {
                // 若已在之前的注视点范围内,还需判断是否属于同一素材,若不是也要生成新的统计注视点 */
                if (changeMaterial) {
                    statisticData = calcStatisticFixationData(alreadyFixStatList, currentScheme, currentMaterial,
                            currentResult, baseTime);
                }
            }
            alreadyFixStatList.add(waitGazeData); // 将新的数据加入数据集
        }

        if (changeMaterial) {
            if (materialIndex.index < (currentScheme.getMaterials().size() - 1)) {
                materialIndex.index++; // 对素材索引号做个保护,最大索引不能超过素材个数
            }
        }

        return statisticData;
    }

    /**
     * 根据已有的GazeDate数据集,生成新的统计注视点信息
     *
     * @param alreadyFixStatList
     * @param currentScheme
     * @param currentMaterial
     * @param currentResult
     * @param baseTime
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public StatisticFixationData calcStatisticFixationData(List<GazeData> alreadyFixStatList, Scheme currentScheme,
                                                           Material currentMaterial, Result currentResult, long baseTime) throws Exception {

        /** 0.若待处理的统计点个数小于下限,则直接丢弃 */
        if (alreadyFixStatList.size() < CommonConstants.AnalysisConstats.STATISTIC_FIXATION_NUMBER_MIN) {
            alreadyFixStatList.clear();
            return null;
        }

        /** 1.遍历所有待处理数据,计算平均值/最大值等指标 */
        GazeData data = null;
        double dispersionX = 0;
        double dispersionY = 0;
        double maxDispersionX = 0;
        double maxDispersionY = 0;

        double leftPupilSize = 0;
        double leftMaxPupilSize = 0;
        double rightPupilSize = 0;
        double rightMaxPupilSize = 0;

        Iterator<GazeData> iterator = alreadyFixStatList.iterator();
        while (iterator.hasNext()) {
            data = iterator.next();

            /** 计算平均坐标 */
            dispersionX += data.smoothedCoordinates.x;
            dispersionY += data.smoothedCoordinates.y;

            /** 计算平均瞳孔和最大瞳孔 */
            leftPupilSize += data.leftEye.pupilSize;
            rightPupilSize += data.rightEye.pupilSize;
            if (leftMaxPupilSize < data.leftEye.pupilSize) {
                leftMaxPupilSize = data.leftEye.pupilSize;
            }
            if (rightMaxPupilSize < data.rightEye.pupilSize) {
                rightMaxPupilSize = data.rightEye.pupilSize;
            }

        }

        /** 2.赋值,生成统计数据 */
        StatisticFixationData statisticData = new StatisticFixationData();

        statisticData.setResultCode(currentResult.getResultCode());
        statisticData.setResultName(currentResult.getResultName());
        statisticData.setMaterialCode(currentMaterial.getCode());
        statisticData.setMaterialName(currentMaterial.getName());
        statisticData.setStartTime(0);
        statisticData.setEndTime(currentScheme.getTotalDuration());
        statisticData.setFixationStart(alreadyFixStatList.get(0).timeStamp - baseTime);
        statisticData.setFixationEnd(alreadyFixStatList.get(alreadyFixStatList.size() - 1).timeStamp - baseTime);
        statisticData.setFixationDuration(statisticData.getFixationEnd() - statisticData.getFixationStart());
        // 注视点坐标取平均坐标
        statisticData.setPositionX(doubleFormat(dispersionX / alreadyFixStatList.size()));
        statisticData.setPositionY(doubleFormat(dispersionY / alreadyFixStatList.size()));

        // 偏差坐标 = 每个点的坐标 与 注视点坐标的绝对值
        double totalDispersionX = 0; // X偏差和
        double totalDispersionY = 0; // Y偏差和
        double temp = 0;
        for (int i = 0; i < alreadyFixStatList.size(); i++) {
            temp = Math.abs(alreadyFixStatList.get(i).smoothedCoordinates.x - statisticData.getPositionX());
            totalDispersionX += temp;
            if (maxDispersionX < temp) {
                maxDispersionX = temp;
            }

            temp = Math.abs(alreadyFixStatList.get(i).smoothedCoordinates.y - statisticData.getPositionY());
            totalDispersionY += temp;
            if (maxDispersionY < temp) {
                maxDispersionY = temp;
            }
        }

        // 平均偏差
        statisticData.setDispersionX(doubleFormat(totalDispersionX / alreadyFixStatList.size()));
        statisticData.setDispersionY(doubleFormat(totalDispersionY / alreadyFixStatList.size()));
        // 最大偏差坐标
        statisticData.setMaxDispersionX(doubleFormat(maxDispersionX));
        statisticData.setMaxDispersionY(doubleFormat(maxDispersionY));

        // 平均瞳孔大小
        statisticData.setLeftPupilSize(doubleFormat(leftPupilSize / alreadyFixStatList.size()));
        statisticData.setRightPupilSize(doubleFormat(rightPupilSize / alreadyFixStatList.size()));
        // 最大瞳孔大小
        statisticData.setLeftMaxPupilSize(doubleFormat(leftMaxPupilSize));
        statisticData.setRightMaxPupilSize(doubleFormat(rightMaxPupilSize));

        /** 3.清空已计算数据集 */
        alreadyFixStatList.clear();

        /** 4. 当注视时长未达到下限值时,抛弃该注视点 */
        if (statisticData.getFixationDuration() < CommonConstants.AnalysisConstats.STATISTIC_FIXATION_DURATION_MIN) {
            return null;
        }

        return statisticData;
    }

    /**
     * 对下一个GazeData进行判断,看是否生成扫视点
     *
     * @param alreadySacStatList
     * @param waitGazeData
     * @param currentScheme
     * @param materialIndex
     * @param baseTime
     * @param currentResult
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public StatisticSaccadeData judgeStatisticSaccadeData(List<GazeData> alreadySacStatList, GazeData waitGazeData,
                                                          Scheme currentScheme, TempIndex materialIndex, Long baseTime, Result currentResult, List<EventModel> eventModels) throws Exception {
        /** 1. 数据准备 */
        Material currentMaterial = currentScheme.getMaterials().get(materialIndex.index);
        long startTime = baseTime + currentMaterial.getOffset(); // 当前素材开始时间
        long endTime = startTime + currentMaterial.getDuration(); // 当前素材结束时间
        // 获取点击事件的开始结束事件，如果点击事件没有则使用方案的开始结束事件
//        for (EventModel model : eventModels){
//            if (currentMaterial.getName().equalsIgnoreCase(model.getMaterialName())){
//                endTime = Long.parseLong(model.getEventTime());
//                startTime = endTime - Long.parseLong(model.getIntervalTime());
//            }
//        }
        boolean changeMaterial = true; // 是否切换了素材
        if (waitGazeData.timeStamp >= startTime && waitGazeData.timeStamp < endTime) {
            changeMaterial = false;
        }

        /** 2.若待处理数据集为空,则不生成统计数据 */
        if (alreadySacStatList.isEmpty()) {
            if (!waitGazeData.isFixated) { //
                alreadySacStatList.add(waitGazeData);
            }
            return null;
        }

        StatisticSaccadeData statisticData = null;
        /** 3.若待处理数据isFixated标识为true,则直接计算已有的数据集,生成新的统计扫视点 */
        if (waitGazeData.isFixated) {
            statisticData = calcStatisticSaccadeData(alreadySacStatList, currentScheme, currentMaterial, currentResult,
                    baseTime);
        } else {
            /** 4.若待处理数据isFixated标识为false,还需进一步分析是否切换了素材 */
            if (changeMaterial) {
                // 若不属于同一素材,则要生成新的统计扫视点 */
                statisticData = calcStatisticSaccadeData(alreadySacStatList, currentScheme, currentMaterial,
                        currentResult, baseTime);
            }

            alreadySacStatList.add(waitGazeData); // 将新的数据加入数据集
        }

        if (changeMaterial) {
            if (materialIndex.index < (currentScheme.getMaterials().size() - 1)) {
                materialIndex.index++; // 对素材索引号做个保护,最大索引不能超过素材个数
            }
        }

        return statisticData;
    }

    /**
     * 根据已有的GazeDate数据集,生成新的统计扫视点信息
     *
     * @param alreadySacStatList
     * @param currentScheme
     * @param currentMaterial
     * @param currentResult
     * @param baseTime
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public StatisticSaccadeData calcStatisticSaccadeData(List<GazeData> alreadySacStatList, Scheme currentScheme,
                                                         Material currentMaterial, Result currentResult, long baseTime) throws Exception {

        /** 1.赋值,生成统计数据 */
        StatisticSaccadeData statisticData = new StatisticSaccadeData();

        statisticData.setResultCode(currentResult.getResultCode());
        statisticData.setResultName(currentResult.getResultName());
        statisticData.setMaterialCode(currentMaterial.getCode());
        statisticData.setMaterialName(currentMaterial.getName());
        statisticData.setStartTime(0);
        statisticData.setEndTime(currentScheme.getTotalDuration());

        // 扫视点开始时间取第1个点的时间,结束时间取最后1个点的时间
        statisticData.setSaccadeStart(alreadySacStatList.get(0).timeStamp - baseTime);
        statisticData.setSaccadeEnd(alreadySacStatList.get(alreadySacStatList.size() - 1).timeStamp - baseTime);
        statisticData.setSaccadeDuration(statisticData.getSaccadeEnd() - statisticData.getSaccadeStart());

        // 扫视点开始坐标取第1个点的坐标,结束坐标取最后1个点的坐标
        statisticData.setStartPositionX(doubleFormat(alreadySacStatList.get(0).smoothedCoordinates.x));
        statisticData.setStardPositionY(doubleFormat(alreadySacStatList.get(0).smoothedCoordinates.y));
        statisticData
                .setEndPositionX(doubleFormat(alreadySacStatList.get(alreadySacStatList.size() - 1).smoothedCoordinates.x));
        statisticData
                .setEndPositionY(doubleFormat(alreadySacStatList.get(alreadySacStatList.size() - 1).smoothedCoordinates.y));

        /** 2.清空已计算数据集 */
        alreadySacStatList.clear();

        /** 3. 当扫视时长等于0时,抛弃该扫视点 */
        if (statisticData.getSaccadeDuration() == 0) {
            return null;
        }

        return statisticData;
    }

    /**
     * 计算点是否包含
     *
     * @param basepoint 基础点
     * @param point     是否被包含的点
     * @return true是 false否
     */
    private static boolean contains(Point2D basepoint, Point2D point) {
        double deviation = CommonConstants.AnalysisConstats.STATISTIC_FIXATION_DEVIATION; // 偏离范围
        double xLength = 0;
        double yLength = 0;

        if (basepoint.x > point.x) {
            xLength = basepoint.x - point.x;
        } else {
            xLength = point.x - basepoint.x;
        }

        if (basepoint.y > point.y) {
            yLength = basepoint.y - point.y;
        } else {
            yLength = point.y - basepoint.y;
        }

        double length = Math.sqrt(xLength * xLength + yLength * yLength); // 勾股定理算两点直接距离

        if (length >= deviation) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 格式化double型数据精度
     *
     * @param value
     * @return
     */
    private static double doubleFormat(double value) {
        return Double.parseDouble(decimalFormat.format(value));
    }

    /**
     * 计算分页和数据填充
     *
     * @param tempDataList   临时全部数据
     * @param resultDataList 最终显示数据
     * @param page           分页条件
     * @return
     */
    private <T> List<T> calcDataList(List<T> tempDataList, List<T> resultDataList, PageParameter page) {
        int totalCount = tempDataList.size();
        if (totalCount >= (page.getCurrentPage() - 1) * page.getPageSize()) {
            int count = 1;
            for (int i = (page.getCurrentPage() - 1) * page.getPageSize(); i < tempDataList.size(); i++) {
                if (count > page.getPageSize()) {
                    break;
                }
                resultDataList.add(tempDataList.get(i));
                count++;
            }
            page.setTotalCount(totalCount);
            int totalPage = totalCount / page.getPageSize() + ((totalCount % page.getPageSize() == 0) ? 0 : 1);
            page.setTotalPage(totalPage);
        }
        return resultDataList;
    }
}
